<template>
  <div class="attachment-manager">
    <div class="attachment-header">
      <h4 class="attachment-title">
        {{ computedTitle }}
      </h4>
      <div class="attachment-actions">
        <slot name="header-actions"></slot>
        <template v-if="showUploadAction">
          <el-upload
            class="attachment-upload"
            :http-request="handleCustomUpload"
            :before-upload="beforeUpload"
            :show-file-list="false"
            :disabled="isUploadDisabled"
          >
            <el-button type="primary" size="small" :loading="uploading">
              <el-icon><Paperclip /></el-icon>
              {{ uploadButtonLabel }}
            </el-button>
          </el-upload>
        </template>
      </div>
    </div>

    <p v-if="uploadTip" class="attachment-tip">
      {{ uploadTip }}
    </p>

    <el-skeleton
      v-if="loading"
      :rows="3"
      animated
      class="attachment-skeleton"
    />

    <template v-else>
      <div v-if="attachments && attachments.length > 0" class="attachment-list">
        <div
          v-for="attachment in attachments"
          :key="getAttachmentKey(attachment)"
          class="attachment-item"
        >
          <div class="attachment-info">
            <el-icon class="attachment-icon"><Paperclip /></el-icon>
            <div class="attachment-meta">
              <div
                class="attachment-name"
                :title="getAttachmentName(attachment)"
              >
                {{ getAttachmentName(attachment) }}
              </div>
              <div class="attachment-sub">
                <span class="attachment-size">
                  {{ formatSize(attachment.file_size) }}
                </span>
                <span class="attachment-time">
                  {{ formatTime(attachment.uploaded_at) }}
                </span>
              </div>
            </div>
          </div>
          <div class="attachment-buttons">
            <el-button
              type="text"
              size="small"
              @click="handleDownload(attachment)"
              :loading="downloadingId === getAttachmentKey(attachment)"
            >
              <el-icon><Download /></el-icon>
              下载
            </el-button>
            <el-button
              v-if="canDeleteAttachment(attachment)"
              type="text"
              size="small"
              @click="handleDelete(attachment)"
              :loading="deletingId === getAttachmentKey(attachment)"
            >
              <el-icon><Delete /></el-icon>
              删除
            </el-button>
          </div>
        </div>
      </div>
      <div v-else class="attachment-empty">
        <slot name="empty">
          <el-empty :image-size="60" :description="emptyText" />
        </slot>
      </div>
    </template>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { UploadRequestOptions } from 'element-plus'
import { Paperclip, Download, Delete } from '@element-plus/icons-vue'

export interface AttachmentItem {
  id?: string
  filename: string
  original_filename?: string
  original_name?: string
  file_size?: number
  file_type?: string
  url?: string
  uploaded_by?: string
  uploaded_at?: string | Date
  [key: string]: unknown
}

interface AttachmentMessages {
  uploadSuccess?: string
  uploadError?: string
  deleteSuccess?: string
  deleteError?: string
  downloadSuccess?: string
  downloadError?: string
}

const props = withDefaults(
  defineProps<{
    attachments: AttachmentItem[]
    title?: string
    showCount?: boolean
    canUpload?: boolean
    uploadHandler?: (file: File) => Promise<unknown>
    uploadButtonText?: string
    emptyText?: string
    maxFileSizeMb?: number
    uploadTip?: string
    uploadDisabledReason?: string
    canDelete?: boolean | ((attachment: AttachmentItem) => boolean)
    deleteHandler?: (attachment: AttachmentItem) => Promise<unknown>
    confirmBeforeDelete?: boolean
    deleteConfirmTitle?: string
    deleteConfirmMessage?: string | ((attachment: AttachmentItem) => string)
    downloadHandler?: (attachment: AttachmentItem) => Promise<unknown>
    messages?: AttachmentMessages
    loading?: boolean
    sizeFormatter?: (size?: number) => string
    timeFormatter?: (time?: string | Date) => string
  }>(),
  {
    attachments: () => [],
    title: '附件',
    showCount: true,
    canUpload: true,
    uploadButtonText: '上传附件',
    emptyText: '暂无附件',
    maxFileSizeMb: 50,
    uploadTip: '',
    uploadDisabledReason: '',
    confirmBeforeDelete: true,
    deleteConfirmTitle: '确认删除',
    deleteConfirmMessage: undefined,
    messages: () => ({}),
    loading: false
  }
)

const emit = defineEmits<{
  (e: 'upload-success', payload: unknown): void
  (e: 'upload-error', payload: unknown): void
  (e: 'delete-success', attachment: AttachmentItem): void
  (e: 'delete-error', payload: { attachment: AttachmentItem; error: unknown }): void
  (e: 'download-success', attachment: AttachmentItem): void
  (e: 'download-error', payload: { attachment: AttachmentItem; error: unknown }): void
}>()

const uploading = ref(false)
const deletingId = ref<string | null>(null)
const downloadingId = ref<string | null>(null)

const defaultMessages: Required<AttachmentMessages> = {
  uploadSuccess: '附件上传成功',
  uploadError: '附件上传失败，请稍后重试',
  deleteSuccess: '附件删除成功',
  deleteError: '删除附件失败，请稍后重试',
  downloadSuccess: '附件下载已开始',
  downloadError: '下载附件失败，请稍后重试'
}

const resolvedMessages = computed(() => ({
  uploadSuccess: props.messages?.uploadSuccess ?? defaultMessages.uploadSuccess,
  uploadError: props.messages?.uploadError ?? defaultMessages.uploadError,
  deleteSuccess: props.messages?.deleteSuccess ?? defaultMessages.deleteSuccess,
  deleteError: props.messages?.deleteError ?? defaultMessages.deleteError,
  downloadSuccess: props.messages?.downloadSuccess ?? defaultMessages.downloadSuccess,
  downloadError: props.messages?.downloadError ?? defaultMessages.downloadError
}))

const computedTitle = computed(() => {
  if (!props.showCount) return props.title
  const count = props.attachments?.length ?? 0
  return `${props.title} (${count})`
})

const uploadButtonLabel = computed(() => props.uploadButtonText)

const showUploadAction = computed(() => props.uploadHandler && props.canUpload)

const isUploadDisabled = computed(() => {
  if (!showUploadAction.value) return true
  return uploading.value
})

const maxSizeBytes = computed(() => (props.maxFileSizeMb ?? 50) * 1024 * 1024)

const formatSize = (size?: number) => {
  if (props.sizeFormatter) {
    return props.sizeFormatter(size)
  }
  if (!size || size <= 0) return '0 B'
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  const index = Math.floor(Math.log(size) / Math.log(1024))
  const value = size / Math.pow(1024, index)
  return `${Math.round(value * 100) / 100} ${units[index]}`
}

const formatTime = (value?: string | Date) => {
  if (props.timeFormatter) {
    return props.timeFormatter(value)
  }
  if (!value) return ''
  const date = typeof value === 'string' ? new Date(value) : value
  if (Number.isNaN(date.getTime())) return ''
  return date.toLocaleString('zh-CN')
}

const getAttachmentKey = (attachment: AttachmentItem) => attachment.id || attachment.filename

const getAttachmentName = (attachment: AttachmentItem) =>
  attachment.original_filename || attachment.original_name || attachment.filename

const canDeleteAttachment = (attachment: AttachmentItem) => {
  if (!props.deleteHandler) return false
  if (typeof props.canDelete === 'function') {
    return props.canDelete(attachment)
  }
  if (typeof props.canDelete === 'boolean') {
    return props.canDelete
  }
  return true
}

const beforeUpload = (file: File) => {
  if (!showUploadAction.value || !props.uploadHandler) {
    if (props.uploadDisabledReason) {
      ElMessage.warning(props.uploadDisabledReason)
    }
    return false
  }

  const maxSize = maxSizeBytes.value
  if (file.size > maxSize) {
    ElMessage.error(`文件大小不能超过 ${props.maxFileSizeMb}MB`)
    return false
  }

  uploading.value = true
  return true
}

const handleCustomUpload = async (options: UploadRequestOptions) => {
  if (!props.uploadHandler) {
    const error = new Error('未配置上传处理函数')
    ElMessage.error(resolvedMessages.value.uploadError)
    uploading.value = false
    emit('upload-error', error)
    return Promise.reject(error)
  }

  try {
    const result = await props.uploadHandler(options.file)
    ElMessage.success(resolvedMessages.value.uploadSuccess)
    emit('upload-success', result)
    return result
  } catch (error) {
    ElMessage.error(resolvedMessages.value.uploadError)
    emit('upload-error', error)
    throw error
  } finally {
    uploading.value = false
  }
}

const handleDelete = async (attachment: AttachmentItem) => {
  if (!props.deleteHandler) {
    return
  }

  if (props.confirmBeforeDelete) {
    const confirmMessage =
      typeof props.deleteConfirmMessage === 'function'
        ? props.deleteConfirmMessage(attachment)
        : props.deleteConfirmMessage ||
          `确定要删除附件“${getAttachmentName(attachment)}”吗？`
    try {
      await ElMessageBox.confirm(confirmMessage, props.deleteConfirmTitle, {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
    } catch {
      return
    }
  }

  const key = getAttachmentKey(attachment)
  deletingId.value = key
  try {
    await props.deleteHandler(attachment)
    ElMessage.success(resolvedMessages.value.deleteSuccess)
    emit('delete-success', attachment)
  } catch (error) {
    ElMessage.error(resolvedMessages.value.deleteError)
    emit('delete-error', { attachment, error })
  } finally {
    deletingId.value = null
  }
}

const handleDownload = async (attachment: AttachmentItem) => {
  if (!props.downloadHandler) {
    emit('download-error', { attachment, error: new Error('未配置下载处理函数') })
    ElMessage.error(resolvedMessages.value.downloadError)
    return
  }

  const key = getAttachmentKey(attachment)
  downloadingId.value = key
  try {
    await props.downloadHandler(attachment)
    ElMessage.success(resolvedMessages.value.downloadSuccess)
    emit('download-success', attachment)
  } catch (error) {
    ElMessage.error(resolvedMessages.value.downloadError)
    emit('download-error', { attachment, error })
  } finally {
    downloadingId.value = null
  }
}
</script>

<style scoped lang="scss">
.attachment-manager {
  display: flex;
  flex-direction: column;
  gap: 12px;

  .attachment-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;

    .attachment-title {
      margin: 0;
      font-size: 16px;
      font-weight: 600;
      color: #303133;
    }

    .attachment-actions {
      display: flex;
      align-items: center;
      gap: 12px;
    }
  }

  .attachment-tip {
    margin: 0;
    font-size: 13px;
    color: #909399;
  }

  .attachment-list {
    display: flex;
    flex-direction: column;
    gap: 12px;

    .attachment-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 12px 16px;
      border: 1px solid #ebeef5;
      border-radius: 6px;
      background-color: #fafafa;

      .attachment-info {
        display: flex;
        align-items: center;
        gap: 12px;
        flex: 1;
        min-width: 0;

        .attachment-icon {
          color: #409eff;
        }

        .attachment-meta {
          display: flex;
          flex-direction: column;
          gap: 6px;
          min-width: 0;

          .attachment-name {
            font-size: 14px;
            font-weight: 500;
            color: #303133;
            max-width: 360px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }

          .attachment-sub {
            display: flex;
            gap: 12px;
            font-size: 12px;
            color: #909399;
          }
        }
      }

      .attachment-buttons {
        display: flex;
        align-items: center;
        gap: 4px;
        flex-shrink: 0;
      }
    }
  }

  .attachment-empty {
    padding: 12px 0;
    border: 1px dashed #dcdfe6;
    border-radius: 6px;
    background-color: #fafafa;
  }

  .attachment-skeleton {
    padding: 12px 0;
  }
}
</style>

